#include "Model.h"
#include <cassert>

namespace SemanticModel
{
	Model::Model()
	{
	}

	inline std::wstring prepareName(const wchar_t* name_)
	{
		std::wstring name = name_;
		if (name.size())
			name[0] = tolower(name[0]);
		return name;
	}

	bool Model::addVertex(const wchar_t* name_, NodeType type)
	{
		std::wstring name = prepareName(name_);
		if (_names.find(name) != _names.end())
			return false;
		vertex_descriptor desc = boost::add_vertex(VertexProperty(name, VertexBundle(Node(type))), *this);

		_names[name] = desc;
		_vertexes[desc] = std::wstring(name);
		return true;
	}

	void Model::addRelation(const wchar_t* roleA_, const Relation& relation, const wchar_t* roleB_)
	{
		std::wstring roleA = prepareName(roleA_);
		assert(_names.find(roleA) != _names.end());
		vertex_descriptor roleADesc = _names.at(roleA);
		
		std::wstring roleB = prepareName(roleB_);
		assert(_names.find(roleB) != _names.end());
		vertex_descriptor roleBDesc = _names.at(roleB);		

		boost::add_edge(roleADesc, roleBDesc, EdgeProperty(relation), *this);
	}

	Model::Nodes Model::nodes(NodeType type) const
	{
		NodeIterator begin(*this, boost::vertices(*this).first, boost::vertices(*this).second, type);
		NodeIterator end(*this, boost::vertices(*this).second, boost::vertices(*this).second, type);
		return Nodes(begin, end);
	}

	Model::RelationsAll Model::relations(RelationType type) const
	{
		boost::graph_traits<Graph>::edge_iterator ei, ei_end;
		tie(ei, ei_end) = boost::edges(*this);

		RelationAll begin(*this, ei, ei_end, _vertexes, type);
		RelationAll end(*this, ei_end, ei_end, _vertexes, type);
		return RelationsAll(begin, end);
	}
	
	Model::RelationsOut Model::relations(const NodeIterator& nodeFrom,
		RelationType type/* = NoneRelation*/) const
	{
		vertex_descriptor desc = *nodeFrom.getPointer();
		return relations(desc, type);
	}

	
	Model::RelationsOut Model::relations(const wchar_t* nodeFrom_, RelationType type /*= NoneRelation*/) const
	{
		std::wstring nodeFrom = prepareName(nodeFrom_);
		vertex_descriptor desc = _names.at(nodeFrom);
		return relations(desc, type);
	}

	Model::RelationsOut Model::relations(vertex_descriptor nodeFrom, RelationType type /*= NoneRelation*/) const
	{
		RelationOut begin(*this, boost::out_edges(nodeFrom, *this).first, boost::out_edges(nodeFrom, *this).second, _vertexes, type);
		RelationOut end(*this, boost::out_edges(nodeFrom, *this).second, boost::out_edges(nodeFrom, *this).second, _vertexes, type);
		return RelationsOut(begin, end);
	}
}
